home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / COMPILER / SATHER / !Sather / Library / Containrs / sa / hashtab < prev    next >
Text File  |  1996-07-16  |  12KB  |  429 lines

  1. ---------------------------> Sather 1.1 source file <--------------------------
  2. -- $Id: hashtab.sa,v 1.7 1996/07/16 04:38:14 holger Exp $
  3. -- Author: Holger Klawitter <holger@math.uni-muenster.de>
  4. --
  5. -- hashtab.sa:
  6. -- DYNAMIC_BUCKET_TABLE{K,BUCKET}
  7. -- DYNAMIC_DATABUCKET_TABLE{K,BUCKET}
  8. --   The dynamic hash table used as basis for container classes 
  9. --   H_SET, H_MAP, H_BAG and H_MULTIMAP.
  10. --   Unlike normal hash tables a dynamic hash table can do fast shrinking.
  11. --   See: Per-Ake Larson; Communications of the ACM Vol.31 (1988) P.446-457
  12. -- BUCKET{E}
  13. -- DATABUCKET{K,E}
  14. --   Implementation of the cell contents of the dynamic hashs table. BUCKETs
  15. --   are not used in the interface of the hash table.
  16. -- $BUCKET{E,ME}
  17. --   Type bound for buckets in a dynamic hash table.
  18. ---------------------------------------------------------------------------
  19. class DYNAMIC_BUCKET_TABLE{E,BUCKET<$BUCKET{E,BUCKET}} is
  20.    -- Data structure used to implement a hash table.
  21.    -- Per-Ake Larson; Communications of the ACM Vol.31 (1988) P.446-457
  22.    -- The directory/segment structure is changed in favor of a dymnamically
  23.    -- changing array as storage area.
  24.     include COMPARE{E} elt_eq->elt_key_eq,elt_hash->elt_key_hash,
  25.       elt_nil->elt_key_nil,elt_lt->,is_elt_nil->;
  26.    
  27.     private shared lower_fill_ratio: FLT := 0.800;
  28.     private shared upper_fill_ratio: FLT := 1.000;
  29.     -- For fast access one needs a low ratio between number of elements in the 
  30.     -- and number of cells. For efficient memory usage one needs a high ratio.
  31.     -- The ratio should always be between these two bounds (unless the table
  32.     -- is really small; where the ratio can get even lower.)
  33.  
  34.     private attr store: AREF{BUCKET};
  35.     -- Here is the data being stored.
  36.  
  37.     private attr doubles: INT;
  38.     -- Number of times the initial table size has been doubled.
  39.  
  40.     private attr split_pos: INT;
  41.     -- Position of the next bucket to split.
  42.  
  43.     private attr bound: INT;
  44.     -- Upper bound for split_pos. Is always initial_size * 2.pow(doubles)
  45.  
  46.     private attr asize: INT;
  47.     -- The size of the fraction of the store which is currently in use.
  48.     -- Array access beyond this bound is illegal.
  49.  
  50.     private attr minsize: INT;
  51.     -- Lower bound for the store size.
  52.  
  53.     readonly attr n_inds: INT;
  54.     -- Returns the number of elements (resp. indices) in the table.
  55.  
  56.     private hash(e:E): INT  pre ~void(self) is
  57.     -- Returns the bucket to store e. This number will be
  58.     -- generated from the hash-value and be normailzed through
  59.     -- the actual size of the set.
  60.     h,res: INT;
  61.     
  62.     h := elt_key_hash(e);
  63.     res := h % bound;
  64.     if res >= split_pos then return res end;
  65.     return h % ( bound.lshift(1) )
  66.     end;
  67.     
  68.     set_bucket(i:INT,l:BUCKET)
  69.       pre 0 <= i and i < asize and ~void(self)
  70.     is 
  71.     store[i]:=l
  72.     end;
  73.  
  74.     bucket(i:INT): BUCKET
  75.       pre 0 <= i and i < asize and ~void(self)
  76.     is 
  77.     return store[i]
  78.     end;
  79.  
  80.     create: SAME
  81.     is
  82.     if void(self) then return create_sized(16) end;
  83.     return create_sized(minsize.rshift(1))
  84.     end;
  85.  
  86.     create_sized(initial_size:INT): SAME 
  87.     -- Creating a table with another minimal size. This might be useful to avoid
  88.     -- shrinking of large table which might get very empty.
  89.       pre initial_size.is_even and initial_size > 0
  90.     is
  91.     res ::= new;
  92.     res.store := #AREF{BUCKET}(initial_size.lshift(2));
  93.     res.bound := initial_size;
  94.     res.asize := initial_size.lshift(1);
  95.     res.minsize := initial_size.lshift(1);
  96.     return res
  97.     end;
  98.  
  99.    map_copy: SAME    pre ~void(self) is
  100.       -- Returns a copy of self with all properties set like self.
  101.       res ::= new;
  102.       res.store := store.create(store.asize);
  103.       res.asize := asize;
  104.       res.n_inds := n_inds;
  105.       res.minsize := minsize;
  106.       res.bound := bound;
  107.       res.doubles := doubles;
  108.       res.split_pos := split_pos;
  109.       loop
  110.      i ::= 0.upto!(asize-1);
  111.      res.store[i] := store[i].copy_list;
  112.       end;
  113.       return res
  114.    end;
  115.  
  116.     -- The functions changing the size of the bucket table:
  117.     -- They are split into three steps.
  118.     -- 1.) Splitting the next bucket into two (update_*)
  119.     -- 2.) Resizing the storage area. (shrink/grow)
  120.     -- 3.) Using the next storage cell for the new bucket. (update_*)
  121.  
  122.     private grow pre ~void(self) is 
  123.     -- Increases the size of the array by one.
  124.     -- The functions changing the size of the bucket table:
  125.     -- They are split into two parts.
  126.     -- 1.) Splitting the next bucket into two. (update_*)
  127.     -- 2.) Resizing the storage area. (shrink/grow)
  128.     if store.asize = asize then
  129.         news ::= store.create(asize.lshift(1));
  130.         loop news.aset!(store.aelt!) end;
  131.         store := news
  132.     end;
  133.     asize := asize + 1;
  134.     end;
  135.  
  136.    private shrink  pre ~void(self) is
  137.     -- Decreases the size of the array by one.
  138.     -- Resizes the storage area, if neccessary.
  139.     if asize = minsize then return end;
  140.     if store.asize = asize.lshift(1) then
  141.         news ::= store.create(asize);
  142.         loop news.aset!(store.aelt!) end;
  143.         store := news
  144.     end;
  145.     asize := asize - 1;
  146.     end;
  147.  
  148.     private update_insert is
  149.     -- Checks the actual fill ratio of the set.
  150.     -- Adds a bucket to the hash table if the ratio is high enough. 
  151.     -- The functions changing the size of the bucket table
  152.     -- are split into two parts.
  153.     -- 1.) Splitting the next bucket into two. (update_*)
  154.     -- 2.) Resizing the storage area. (shrink/grow)
  155.     if n_inds.flt / (bound+split_pos).flt < upper_fill_ratio then
  156.         return
  157.     end;
  158.     
  159.     cur ::= bucket(split_pos);
  160.     prev ::= cur; prev := void;
  161.     -- This is an ugly hack to make prev to have the same type
  162.     -- like cur.
  163.     loop until!( void(cur) );
  164.         if elt_key_hash(cur.item) % ( bound.lshift(1) ) = split_pos
  165.         then -- keep in the old bucket
  166.         prev := cur;
  167.         cur := cur.next;
  168.         else -- put into new bucket
  169.         if void(prev) then -- the first one for new bucket
  170.             set_bucket(split_pos, cur.next);
  171.             cur.next( bucket(bound + split_pos) );
  172.             set_bucket( bound + split_pos, cur );
  173.             cur := bucket( split_pos )
  174.         else
  175.             prev.next(cur.next);
  176.             cur.next( bucket( bound + split_pos ));
  177.             set_bucket( bound + split_pos, cur );
  178.             cur := prev.next
  179.         end
  180.         end    
  181.     end;
  182.     grow;
  183.     split_pos := split_pos + 1;
  184.     if split_pos = bound then
  185.         split_pos := 0;
  186.         doubles := doubles + 1;
  187.         bound := bound.lshift(1);
  188.     end
  189.     end; -- update_insert
  190.     
  191.     private update_delete is
  192.     -- Checks the actual fill ratio of the set.
  193.     -- Removes a bucket from the hash table if the ratio is low enough.
  194.     if n_inds.flt / (bound+split_pos).flt > lower_fill_ratio then
  195.         return
  196.     end;
  197.     
  198.     --      to_merge ::= bucket(split_pos);
  199.     --      if void(to_merge) then -- just get the other bucket
  200.     --     set_bucket( split_pos, bucket( bound + split_pos ))
  201.     --      else 
  202.     --     to_merge.append( bucket( bound + split_pos ) )
  203.     --      end;
  204.     --      set_bucket( bound + split_pos, void );
  205.     --      shrink;
  206.     split_pos := split_pos - 1;
  207.     if split_pos < 0 then
  208.         if doubles = 0 then
  209.         split_pos := 0
  210.         else
  211.         doubles := doubles - 1;
  212.         bound := bound.rshift(1);
  213.         split_pos := bound - 1;
  214.         end
  215.     end;
  216.     shrink;
  217.     to_merge ::= bucket(split_pos);
  218.     if void(to_merge) then -- just get the other bucket
  219.         set_bucket( split_pos, bucket( bound + split_pos ))
  220.     else 
  221.         to_merge.append( bucket( bound + split_pos ) )
  222.     end;
  223.     set_bucket( bound + split_pos, void );
  224.     end; -- update_delete
  225.    
  226.     dbg: STR
  227.     -- Returns an interal string representation of the hashtable.
  228.     -- For debugging only.
  229.     is
  230.     res:STR;
  231.     res := "split_pos=" + split_pos + ", bound=" + bound +
  232.           ", asize=" + asize + ", size=" + n_inds + ", minsize=" +
  233.           minsize + "\n";
  234.     loop
  235.         l ::= store.aelt!;
  236.         bkt: STR := "";
  237.         itemstr: STR;
  238.         item ::= l.list!.item;
  239.         typecase item
  240.         when $STR then itemstr := item.str
  241.         else itemstr := SYS::id(item).str; end;
  242.         loop bkt := bkt + ",".separate!(itemstr) end;
  243.         res := res + ", ".separate!(0.up!.str+"="+bkt);
  244.     end;
  245.     return res+"\n";
  246.     end; -- dbg
  247.     
  248. end; -- DYNAMIC_BUCKET_TABLE
  249. ---------------------------------------------------------------------------
  250. class DYNAMIC_DATABUCKET_TABLE{K,E}
  251. -- This version of a DYNAMIC_BUCKET_TABLE does not simply store elements,
  252. -- it stores datas and keys seperately in each bucket, instead.
  253. -- Used in H_BAG, H_MAP and H_MULTIMAP.
  254. is
  255.     include DYNAMIC_BUCKET_TABLE{K,DATABUCKET{K,E}};   
  256.    
  257.     private data_nil: E is
  258.     e: E;
  259.     typecase e
  260.     when $NIL then 
  261.         temp::= e.nil; typecase temp when E then return temp end;
  262.     else return void   end
  263.     end;
  264.     
  265.     map_aset(k:K,e:E) pre ~void(self) is
  266.     -- Over-ride data if 'k' exists.
  267.     -- Otherwise grow the bucket chain associated with hash(k)
  268.     h ::= hash(k);
  269.     loop
  270.         b ::= bucket(h).list!;
  271.         if elt_key_eq(b.item,k) then b.data := e; return end
  272.     end;
  273.     set_bucket( h, #DATABUCKET{K,E}(k,e,bucket(h)) );
  274.     n_inds := n_inds + 1;
  275.     update_insert
  276.     end;
  277.     
  278.     map_delete(k:K): E  pre ~void(self) is
  279.     -- Removes an element from the set.
  280.     -- Does nothing if there is no such element.
  281.     h ::= hash(k);
  282.     b ::= bucket(h);
  283.     prev ::= b; prev := void; -- NASTY HACK to force type inference on prev.
  284.     
  285.     loop until!( void(b) or elt_key_eq(b.item,k) );
  286.         prev := b;
  287.         b := b.next
  288.     end;
  289.     if void(b) then
  290.         return data_nil
  291.     end;
  292.     res ::= b.data;
  293.     if void(prev) then  set_bucket( h, b.next )
  294.     else prev.next(b.next)   end;
  295.     n_inds := n_inds - 1;
  296.     update_delete;
  297.     return res
  298.     end; -- map_delete
  299.     
  300.     map_has_ind(k:K): BOOL  pre ~void(self) is
  301.     loop
  302.         if elt_key_eq(bucket(hash(k)).list!.item,k) then  return true end
  303.     end;
  304.     return false
  305.     end;
  306.     
  307.     map_aget(k:K): E  pre ~void(self) is
  308.     -- Returns the element equal to 'e' from the set.
  309.     -- Returns void or T::nil if there is no such element.
  310.     -- Self may not be void.
  311.     loop
  312.         b ::= bucket(hash(k)).list!;
  313.         if elt_key_eq(b.item,k) then return b.data end
  314.     end;
  315.     return data_nil
  316.     end;
  317.     
  318.     map_key!: K 
  319.       pre ~void(self) 
  320.     is
  321.     loop
  322.         b ::= bucket( 0.upto!(bound+split_pos-1) );
  323.         loop yield b.list!.item end
  324.     end
  325.     end;
  326.     
  327.     map_elt!: E  pre ~void(self) is
  328.     loop
  329.         b ::= bucket( 0.upto!(bound+split_pos-1) );
  330.         loop yield b.list!.data end
  331.     end
  332.     end;
  333.     
  334.     map_pair!: TUP{K,E} pre ~void(self) is
  335.     loop
  336.         b ::= bucket( 0.upto!(bound+split_pos-1) );
  337.         loop
  338.         bk ::= b.list!;
  339.         yield #TUP{K,E}(bk.item,bk.data)
  340.         end
  341.     end
  342.     end;
  343.     
  344. end; -- DYNAMIC_DATABUCKET_TABLE
  345. ---------------------------------------------------------------------------
  346. abstract class $BUCKET{E,ME<$BUCKET{E,ME}} < $NEXT{ME}
  347. is
  348.     item: E;
  349.     copy_list: SAME;
  350.     list!: SAME;
  351. end; -- $BUCKET{E,SELF}
  352. ---------------------------------------------------------------------------
  353. class BUCKET{E} < $BUCKET{E,BUCKET{E}}  is
  354.    -- Mainly this class adds an item to NEXT and gives some handy constructors.
  355.    include NEXT{SAME};
  356.  
  357.    attr item: E;
  358.    
  359.    create(e:E): SAME is
  360.       -- Create a link containing e.
  361.       res ::= new;
  362.       res.item := e;
  363.       return res
  364.    end;
  365.  
  366.    create(e:E,n:SAME): SAME is
  367.       -- Create a link containig e and prepending the link n.
  368.       -- n may be void.
  369.       res ::= new;
  370.       res.item := e; res.next := n;
  371.       return res
  372.    end;
  373.  
  374.    copy_list: SAME is
  375.       -- Returns a copy of self and all following links. The
  376.       -- objects will not be 'copy'ed.
  377.       -- Self may be void.
  378.       if void(self) then return void end;
  379.       return #SAME(item,next.copy_list)
  380.    end;
  381.  
  382.    list!: SAME is
  383.       -- Yields all subsequent elements in the list.
  384.       b ::= self;
  385.       loop until!( void(b) ); yield b; b := b.next end
  386.    end;
  387.  
  388. end;
  389. ---------------------------------------------------------------------------
  390. class DATABUCKET{K,E} < $BUCKET{K,DATABUCKET{K,E}} is
  391.    -- An addition to BUCKET{K} this class also adds data.
  392.  
  393.    include NEXT{SAME};
  394.    
  395.    attr item: K;
  396.    attr data: E;
  397.    
  398.    create(k:K): SAME is
  399.       res ::= new;
  400.       res.item := k;
  401.       return res
  402.    end;
  403.    
  404.    create(k:K,e:E): SAME is
  405.       res ::= new;
  406.       res.item := k; res.data := e;
  407.       return res
  408.    end;
  409.    
  410.    create(k:K,e:E,n:SAME): SAME is
  411.       res ::= new;
  412.       res.item := k; res.data := e; res.next := n;
  413.       return res
  414.    end;
  415.  
  416.    copy_list: SAME  is
  417.       if void(self) then return void end;
  418.       return #SAME(item,data,next.copy_list)
  419.    end;
  420.  
  421.    list!: SAME is
  422.       -- Yields all subsequent elements in the list.
  423.       b ::= self;
  424.       loop until!( void(b) ); yield b; b := b.next end
  425.    end;
  426.  
  427. end; -- DATABUCKET{K,E}
  428. ----------------------------------------------------------------------
  429.